package storage

import (
	"bytes"
	"io"

	"gitee.com/thubcc/p2pgit/types"
	"gopkg.in/src-d/go-git.v4/plumbing"
)

// RawObject raw git object
type RawObject struct {
	hash types.Hash
	raw  []byte
	t    plumbing.ObjectType
}

var zeroObject = RawObject{
	hash: types.ZeroHash,
	raw:  []byte{},
	t:    plumbing.InvalidObject,
}

//ObjectStorer the basic func support storage
type ObjectStorer interface {
	GetRawObject(types.Hash) (*RawObject, error)
}

// Storage implement storage
type Storage struct {
	geter types.SomeGeter
	cache map[types.Hash]*RawObject
}

//NewStorage new storage with geter
func NewStorage(gs types.SomeGeter) *Storage {
	return &Storage{
		geter: gs,
		cache: make(map[types.Hash]*RawObject),
	}
}

// GetRawObject get raw object
func (s *Storage) GetRawObject(h types.Hash) (o *RawObject, err error) {
	var ok bool
	if o, ok = s.cache[h]; ok {
		return
	}
	var body []byte
	var t plumbing.ObjectType
	body, t, err = types.ResolveObj(s.geter, types.HashSelect(h))
	if err != nil {
		err = nil
		o = &zeroObject
	} else {
		o = &RawObject{
			raw:  body,
			t:    t,
			hash: h,
		}
		s.cache[h] = o
	}
	return
}

// Type object type
func (o *RawObject) Type() plumbing.ObjectType {
	if o == nil {
		o = &zeroObject
	}
	return o.t
}

// Hash object hash
func (o *RawObject) Hash() types.Hash {
	if o == nil {
		o = &zeroObject
	}
	return o.hash
}

// Size object size
func (o *RawObject) Size() int {
	if o == nil {
		o = &zeroObject
	}
	return len(o.raw)
}

// Reader to reader
func (o *RawObject) Reader() io.Reader {
	if o == nil {
		o = &zeroObject
	}
	return bytes.NewReader(o.raw)
}

// Bytes to bytes
func (o *RawObject) Bytes() []byte {
	if o == nil {
		o = &zeroObject
	}
	return o.raw
}
